import 'dart:async';

/// 统一的 Dart 侧文件事件总线。
///
/// 提供跨模块的文件事件发布/订阅能力，事件结构为 Map：
/// - `type`: 事件类型（`created`/`modified`/`deleted`）；
/// - `path`: 文件绝对路径；
/// - `sizeBytes?`: 文件大小（可选）；
/// - `modifiedMs?`: 修改时间（毫秒，可选）；
/// - `tsMs`: 事件时间戳（毫秒）。
///
/// 内置简单去重：对同一路径同类型，在短时间窗口内（默认 500ms）忽略重复。
class FileEventBus {
  FileEventBus._internal();
  static final FileEventBus instance = FileEventBus._internal();

  final StreamController<Map<String, dynamic>> _controller =
      StreamController<Map<String, dynamic>>.broadcast();

  /// 最近一次事件缓存：`key = "$type|$path" -> tsMs`。
  final Map<String, int> _recent = <String, int>{};
  int _dedupeWindowMs = 500;

  /// 订阅事件流。
  Stream<Map<String, dynamic>> get stream => _controller.stream;

  /// 设置去重窗口时长（毫秒）。
  void setDedupeWindowMs(int ms) {
    _dedupeWindowMs = ms <= 0 ? 0 : ms;
  }

  /// 发布创建事件。
  void emitCreated(String path, {int? sizeBytes, int? modifiedMs, int? tsMs}) {
    final now = tsMs ?? DateTime.now().millisecondsSinceEpoch;
    _emit({
      'type': 'created',
      'path': path,
      'sizeBytes': sizeBytes,
      'modifiedMs': modifiedMs,
      'tsMs': now,
    });
  }

  /// 发布修改事件。
  void emitModified(String path, {int? sizeBytes, int? modifiedMs, int? tsMs}) {
    final now = tsMs ?? DateTime.now().millisecondsSinceEpoch;
    _emit({
      'type': 'modified',
      'path': path,
      'sizeBytes': sizeBytes,
      'modifiedMs': modifiedMs,
      'tsMs': now,
    });
  }

  /// 发布删除事件。
  void emitDeleted(String path, {int? tsMs}) {
    final now = tsMs ?? DateTime.now().millisecondsSinceEpoch;
    _emit({
      'type': 'deleted',
      'path': path,
      'tsMs': now,
    });
  }

  /// 内部：带去重的事件分发。
  void _emit(Map<String, dynamic> e) {
    final type = (e['type'] as String?) ?? '';
    final path = (e['path'] as String?) ?? '';
    if (type.isEmpty || path.isEmpty) return;
    final key = '$type|$path';
    final now = (e['tsMs'] as int?) ?? DateTime.now().millisecondsSinceEpoch;
    final last = _recent[key];
    if (last != null && _dedupeWindowMs > 0) {
      if ((now - last) <= _dedupeWindowMs) return; // drop duplicate
    }
    _recent[key] = now;
    _controller.add(e);
  }
}
